package org.acooke.uykfd.db

import java.sql.Connection

import org.jgrapht.alg.FloydWarshallShortestPaths
import org.jgrapht.Graph
import org.jgrapht.Graphs
import org.jgrapht.graph.DefaultWeightedEdge

import scala.collection.JavaConversions._


trait LfmArtistDistanceRow
  extends WeightedEdgeRow[ArtistRow]

trait LfmArtistDistanceTable
  extends WeightedEdgeTable[
    LfmArtistDistanceRow, ArtistRow, Schema.Artists, LfmArtistDistanceTable, Schema.LfmArtistDistances
  ] {

  def lookupNodes(source: ArtistRow, sink: ArtistRow, cnxn: Connection): Option[LfmArtistDistanceRow]
  def maxWeight(cnxn: Connection): Option[Int]
  def build(graph: Graph[Int, DefaultWeightedEdge], cnxn:Connection)
  def extend(graph: Graph[Int, DefaultWeightedEdge], cnxn:Connection)
  def show(source: String, sink: String, cnxn: Connection)
  def show(source: String, cnxn: Connection)
}

private class LfmArtistDistanceFactory
  extends WeightedEdgeFactory[
    ArtistRow, Schema.Artists, LfmArtistDistanceRecord, LfmArtistDistanceTable, Schema.LfmArtistDistances](
    Schema.LFM_ARTIST_DISTANCES, Schema.ARTISTS, Schema.ARTISTS.orm.fromId)
  with LfmArtistDistanceTable {

  def rowFactory = new LfmArtistDistanceRecord(nodeFactory)

  def build(graph: Graph[Int, DefaultWeightedEdge], cnxn: Connection) =
    fromShortestPaths(
      graph, new FloydWarshallShortestPaths[Int, DefaultWeightedEdge](graph),
      (distance: Double, diameter: Double) => 
        if (distance.isInfinity) None else Some(Math.rint(distance).toInt),
      cnxn)

  def extend(graph: Graph[Int, DefaultWeightedEdge], cnxn: Connection) =
    addShortestPaths(
      graph, new FloydWarshallShortestPaths[Int, DefaultWeightedEdge](graph),
      (distance: Double, diameter: Double) => 
        if (distance.isInfinity) None else Some(Math.rint(distance).toInt),
      cnxn)

  def show(source: String, sink: String, cnxn: Connection) = {
    (Schema.ARTISTS.orm.lookupValue(source, cnxn), 
     Schema.ARTISTS.orm.lookupValue(sink, cnxn)) match {
       case (Some(source), Some(sink)) =>
         lookupNodes(source, sink, cnxn) match {
           case Some(row) => println(row.weight)
           case None => println("unconnected")
         }
       case (None, _) => println(source + " not found")
       case (_, None) => println(sink + " not found")
     }
  }

  def show(source: String, cnxn: Connection) = {
    Schema.ARTISTS.orm.lookupValue(source, cnxn) match {
      case Some(source) =>
        for (forwards <- fromSource(source, cnxn)) {
          val sink = forwards.sink(cnxn)
          lookupNodes(sink, source, cnxn) match {
            case Some(backwards) => println(sink.value + " " + forwards.weight + " (" + backwards.weight + ")")
            case None => println(sink.value + " " + forwards.weight)
          }
        }
      case None => println(source + " not found")
    }
  }

}

private class LfmArtistDistanceRecord(nf: (Int, Connection) => ArtistRow)
  extends WeightedEdgeRecord[
    ArtistRow, Schema.Artists, LfmArtistDistanceRecord, LfmArtistDistanceTable, 
    Schema.LfmArtistDistances
  ](Schema.LFM_ARTIST_DISTANCES, nf)
  with LfmArtistDistanceRow
